﻿using System;
using System.Collections.Generic;
using System.Drawing.Design;
using System.Linq;
using System.Windows.Forms.Design;
using devtm.Editor.TypeSelector;
using DslModeling = global::Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling;
using System.Windows.Forms;
using System.Reflection;
using CustomTypes.Mono.Extensions;
using Mono.Cecil;

namespace devtm.AutoMapper
{
    public class ListeMemberEditor : UITypeEditor
    {

        protected DslModeling.Store Store;
        protected ItemMap Instance;


        public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
        {

            

            IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
            if (edSvc != null)
            {


                bool isSource = context.PropertyDescriptor.Name.Contains("Source");

                if (context.Instance.GetType().IsArray)
                    Instance = ((Object[])context.Instance)[0] as ItemMap;
                else
                    Instance = context.Instance as ItemMap;

                Store = Instance.Store;
                Mapper mapper = GetMapper(context);
                TypeElement typeElement = GetTypeElement(context, mapper);

                if (typeElement != null)
                {
                    using (CustomTypeRepository repository = CustomTypeRepository.Instance(typeElement.Store, false))
                    {

                        TypeDefinition type = repository.GetType(typeElement.AssemblyQualifiedName);

                        TreeView control = new TreeView();

                        TreeNode t =
                            isSource
                            ? new TreeNode() { Text = type.Name, Tag = type, ToolTipText = "this" }
                            : new TreeNode() { Text = type.Name };


                        try
                        {

                            GetNodeMembers(type, t, isSource);
                            control.Nodes.Add(t);
                            t.Expand();

                            if (isSource)
                                BuildSubProperties(t, type);

                        }
                        catch (Exception)
                        {

                            throw;
                        }


                        int indx = 0;
                        if (!string.IsNullOrEmpty(value as string))
                        {
                            string[] tb = (value as string).Split('.');
                            FindProperty(tb, control, t, ref indx);
                        }

                        if (isSource)
                            control.BeforeExpand +=
                                (sender, e) =>
                                {
                                    BuildSubProperties(e.Node, type);
                                };

                        var d = DateTime.Now;


                        control.AfterSelect +=
                        (sender, e) =>
                        {

                            if (DateTime.Now.Subtract(d).Seconds < 1)
                                return;

                            try
                            {

                                MemberReference mm = control.SelectedNode.Tag as MemberReference;

                                if (mm == null || value as string == control.SelectedNode.ToolTipText)
                                    return;

                                value = control.SelectedNode.ToolTipText;

                                using (Transaction trans = Store.TransactionManager.BeginTransaction("Update Type"))
                                {

                                    Instance.NeedMap = MappingHelper.ResolveNeedMap(mm.GetTypeFrom(type));

                                    if (isSource)
                                    {
                                        Instance.SourceType = TypeDefinitionHelper.FindTypeName(mm, type);
                                        MappingHelper.SetInfoTypeSource(Instance, mm.GetTypeFrom(type));
                                    }
                                    else
                                    {
                                        Instance.TargetType = TypeDefinitionHelper.FindTypeName(mm, type);
                                        MappingHelper.SetInfoTypeTarget(Instance, mm.GetTypeFrom(type));
                                    }

                                    if (!Instance.Enable)
                                    {

                                        Instance.Enable = !(String.IsNullOrEmpty(Instance.SourceName) || String.IsNullOrEmpty(Instance.TargetName));
                                    
                                    }

                                    trans.Commit();
                                }

                            }
                            catch (Exception)
                            {

                            }


                            if (sender != null)
                                if ((sender as TreeView).SelectedNode != null)
                                    edSvc.CloseDropDown();

                        };

                        edSvc.DropDownControl(control);

                    }

                }
            }

            return value;

        }

        private void BuildSubProperties(TreeNode t, TypeDefinition sealedType)
        {
            foreach (TreeNode t2 in t.Nodes)
            {

                if (t2.Nodes.Count > 0)
                    continue;

                TypeReference type = null;
                PropertyDefinition p1 = t2.Tag as PropertyDefinition;

                if (p1 != null)
                    type = p1.TypeMember;
                else
                    type = (t2.Tag as FieldDefinition).TypeMember;



                var type2 = type.Resolve();

                if (type2 != null)
                {
                    if (type2.IsEnum)
                        continue;
                }
               

                string n = type.FullName;

                if (!NewMapperHelper.types.Contains(n))
                    GetNodeMembers(type, t2, true);

            }
        }

        private void BuildSubProperty(TreeNode t)
        {

                if (t.Nodes.Count > 0)
                    return;

                TypeReference type = null;
                PropertyDefinition p1 = t.Tag as PropertyDefinition;

                if (p1 != null)
                    type = p1.TypeMember;
                else
                    type = (t.Tag as FieldDefinition).TypeMember;

                if (type.Resolve().IsEnum)
                    return;

                string n = type.FullName;

                if (!NewMapperHelper.types.Contains(n))
                    GetNodeMembers(type, t, true);

        }

        private void FindProperty(string[] value, TreeView control, TreeNode t, ref int index)
        {


            foreach (TreeNode item in t.Nodes)
            {
         
                if (item.Text == value[index])
                {
                    control.SelectedNode = item;

                    if (index < value.Length - 1)
                    {
                        index++;

                        if (item.Nodes.Count == 0)
                            BuildSubProperty(item);


                        FindProperty(value, control, item, ref index);
                        
                    }
                    return;

                }
            }

        }

        private void GetNodeMembers(TypeReference type, TreeNode t, bool isSource)
        {

            foreach (IMember item in type.GetMembers(MemberTypes.Field | MemberTypes.Property, "", type))
            {

                if (item.IsPublic && !item.IsStatic)
                {

                    PropertyDefinition prop1 = item as PropertyDefinition;
                    if (prop1 == null || !prop1.HasParameters)
                    {

                            TreeNode t2 = new TreeNode()
                            {
                                Tag = item,
                                Text = item.Name,
                                ToolTipText = GetName(t, item as IMemberDefinition)
                            };

                            t.Nodes.Add(t2);
                       
                    }
                }

            }

        }

        private static string GetName(TreeNode t, IMemberDefinition item)
        {
            if (t.ToolTipText == "this")
                return item.Name;

            return t.Tag != null && !string.IsNullOrEmpty(t.ToolTipText) ? t.ToolTipText + "." + item.Name : item.Name;
        }

        private static Mapper GetMapper(System.ComponentModel.ITypeDescriptorContext context)
        {
            ItemMap p1;

            if (context.Instance.GetType().IsArray)
                p1 = ((Object[])context.Instance)[0] as ItemMap;
            else
                p1 = context.Instance as ItemMap;

            return p1.Mapper;
        }

        //private IEnumerable<MemberInfo> GetMembers(Type type)
        //{
        //    foreach (MemberInfo item in type.GetMembers())
        //        if (item is PropertyInfo || item is FieldInfo)
        //            yield return item;
        //}


        public override bool IsDropDownResizable
        {
            get
            {
                return true;
            }
        }

        public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
        {


            if (context != null)
            {


                if (context.Instance.GetType().IsArray)
                    Instance = ((Object[])context.Instance)[0] as ItemMap;
                else
                    Instance = context.Instance as ItemMap;

                if (Instance != null)
                {
                    Store = Instance.Store;
                    Mapper mapper = GetMapper(context);
                    TypeElement typeElement = GetTypeElement(context, mapper);

                    using (CustomTypeRepository repository = CustomTypeRepository.Instance(this.Store, false))
                    {

                        var type = repository.GetType(typeElement.AssemblyQualifiedName);

                        if (type != null)
                            return UITypeEditorEditStyle.DropDown;

                    }
                }

            }

            return UITypeEditorEditStyle.None;
        }

        private static TypeElement GetTypeElement(System.ComponentModel.ITypeDescriptorContext context, Mapper mapper)
        {
            if (context.PropertyDescriptor.Name.Contains("Source"))
                return mapper.MappingSource.TypeElement;

            return mapper.MappingDestination.TypeElement;

        }


    }
}
